import { IResBundle } from "./IResBundle";
import { ResType, LoadParam, IResItem, LoadArrayParam } from "./ResDefine";
import ResItem from "./ResItem";
import { IResLoader } from "./IResLoader";
import { IBundleManager } from "./IBundleManager";
import { ResCache } from "./ResCache";

/**
 * 1. 负责处理加载逻辑
 * 2. 管理加载项
 * 3. 清理加载项
 */
export class ResLoader implements IResLoader {

    protected resCache: { [bundle: string]: ResCache } = {}



    /**
     * 需要保证在bundle加载之后使用。
     */
    // protected _helper: IResBundle;
    init(...data: any[]): void { }
    private _bundleMgr: IBundleManager;

    constructor(bundle: IBundleManager) {
        this._bundleMgr = bundle
    }

    getBundleMgr() {
        return this._bundleMgr;
    }

    getCache(bundle: string): ResCache {
        let key = bundle ? bundle : 'default'
        let cache = this.resCache[key]
        if (!cache) {
            cache = new ResCache();
            this.resCache[key] = cache;
        }
        return cache;
    }

    getBundle(bundle: string) {
        return this.getBundleMgr().getBundle(bundle)
    }

    /**
     * 清理单个资源
     * @param url 
     * @param type 
     */
    releaseRes(url: string, type: ResType, bundle: string) {
        let ts = this.getKey(url, type);
        let item = this.getCache(bundle).get(ts)
        if (item) {
            item.release()
            this.getCache(bundle).remove(ts);
        } else {
            console.warn('releaseRes item is null  ts  ', ts)
        }
    }

    /**
    * 删除所有资源
    */
    release() {

        let resources: string[] = Object.keys(this.resCache);
        for (let index = 0; index < resources.length; index++) {
            const key = resources[index];
            const element: ResCache = this.resCache[key];
            if (element) {
                element.release();
            } else {
                // console.warn("ResLoader release url  =  is error  ",key)
            }
        }

        // this.helper.clear()

    }



    private getKey(url: string, type: ResType) {
        let key = url + type;
        return key;
    }
    /**
     * 同时加载多个资源。
     * @param list 需要加载的资源列表
     * @param type 需要加载的资源类型，要求所有资源统一类型
     * @param func 加载后的回调
     * @param loader 资源加载管理器，默认是全局管理器。
     */
    loadArray(param: LoadArrayParam) {
        let resCount = 0;
        let list = param.list;
        let callback = (err: any, res: IResItem) => {
            if (param.target.isValid()) {
                if (err) {
                    console.log(err);
                    param.callback(err);
                    return;
                }
                resCount++;
                param.onProgress(resCount, list.length);
            }

        }
        for (let index = 0; index < list.length; index++) {
            const element = list[index];
            this.loadRes({
                url: element, type: param.type, callback: callback, bundle: param.bundle, target: param.target
            });
        }
    }




    /**
     * 直接缓存已有的资源，比如在脚本中挂载的资源。
     * @param url 
     * @param type 
     * @param res 
     */
    cacheRes(url: string, type: ResType, res: any, bundle: string) {
        let ts = this.getKey(url, type);
        let item: ResItem = this.getCache(bundle).get(ts)
        if (!item) {
            let helper = this.getBundleMgr().getBundle(bundle)
            if (helper) {
                item = new ResItem(helper, url, type, bundle);
                item.setRes(res)
                this.getCache(bundle).add(ts, item)
            }

        }
    }


    /**
     * 加载单个文件
     * @param url 
     * @param type 
     * @param callback 
     */
    loadRes(param: LoadParam) {
        let bundle = param.bundle;
        let helper = this.getBundleMgr().getBundle(bundle)
        if (!helper) {
            this.getBundleMgr().getBundleAsync(bundle, (err: any, bundle: IResBundle) => {

                if (err) {
                    if (param.target.isValid()) {
                        param.callback('bundle is null ', null)
                    }
                } else {
                    // this._helper = bundle;
                    this.load(bundle, param)
                }
            }, true)
        } else {
            this.load(helper, param)
        }
    }
    protected waitList: { [key: string]: LoadParam[] } = {}

    protected pushWaitList(key: string, param: LoadParam) {
        let list = this.waitList[key]
        if (!list) {
            list = []
            this.waitList[key] = list;
        }
        list.push(param)
    }

    protected popWaitList(key: string, err: any, item: IResItem) {
        let list = this.waitList[key]
        if (list) {
            while (list.length > 0) {
                let param: LoadParam = list.shift();
                if (param.target.isValid()) {
                    param.callback(err, item)
                }

            }
        }

    }
    private load(helper: IResBundle, param: LoadParam) {
        let url = param.url;
        let type = param.type;
        let ts = this.getKey(url, type);
        let item: ResItem = this.getCache(param.bundle).get(ts)
        // console.log(" loadRes url ", url, ' ts ', ts, ' item ', item);
        if (item && item.isDone()) {
            param.callback(null, item);
            return;
        } else {
            if (item) {//等待加载成功
                this.pushWaitList(ts, param)
                return;
            } else {
                item = new ResItem(helper, url, type, param.bundle);
                this.getCache(param.bundle).add(ts, item)
                // this.resCache[ts] = item;
                this.pushWaitList(ts, param)
            }

        }

        let onProgress = (count: number, total: number) => {
            if (param.target.isValid()) {
                param.onProgress(count, total)
            }
        }

        let func = (err: any, res: any) => {
            item.updateLoadCount();
            if (err) {
                if (item.getLoadCount() <= 3) {
                    helper.loadRes(url, type, func, onProgress);
                } else {
                    console.warn(" res load fail url is " + url, 'bundleName', param.bundle);
                    if (this.resCache[ts]) {
                        this.popWaitList(ts, err, null)
                    }

                }
            } else {

                if (this.resCache[ts]) {
                    item.setRes(res);
                    item.setLoadingFlag(true)
                    this.popWaitList(ts, err, item)
                } else {
                    //处理加载完之前已经删除的资源,在这里不做处理，当用引擎的releaseUnusedAssets也会删除的。
                    // item.subCount();
                }


            }
        }

        helper.loadRes(url, type, func, onProgress);
    }



    /**
     * 获取资源的唯一方式 
     * @param url 
     * @param type 
     */
    getRes(url: string, type: ResType, bundle: string): ResItem {
        let helper = this.getBundleMgr().getBundle(bundle)
        if (!helper) {
            return null;
        }
        let ts = this.getKey(url, type)
        let item = this.getCache(bundle).get(ts)
        if (item) {
            return item
        } else {
            let res = helper.getRes(url, type);
            if (res) { // 如果其他管理器已经加载了资源，直接使用。
                // console.warn(' 其他加载器已经加载了次资源 ', url, res, this.bundleName)
                let item = new ResItem(helper, url, type, bundle);
                item.setRes(res)
                this.getCache(bundle).add(ts, item)
                return item
            } else {
                // console.warn('getRes url ', url, ' ts ', ts, 'type ', type, 'bundleName ', this.bundleName)
            }

        }
        return null;
    }

    releaseUnusedAssets() {
        let resources: string[] = Object.keys(this.resCache);
        for (let index = 0; index < resources.length; index++) {
            const key = resources[index];
            const element: ResCache = this.resCache[key];
            element.releaseUnusedAssets()
        }

    }
}
